package com.jichangxiu.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.jichangxiu.common.annotation.Dynamic;
import com.jichangxiu.common.annotation.Scope;
import com.jichangxiu.common.constant.Constants;
import com.jichangxiu.common.entity.bo.ValidateBos;
import com.jichangxiu.common.exception.ServiceException;
import com.jichangxiu.common.utils.ConvertUtils;
import com.jichangxiu.framework.service.impl.BaseServiceImpl;
import com.jichangxiu.framework.utils.NoUtils;
import com.jichangxiu.framework.utils.SecurityUtils;
import com.jichangxiu.framework.utils.ValidateUtils;
import com.jichangxiu.system.entity.bo.SysRoleBo;
import com.jichangxiu.system.entity.dto.SysRoleMenuDto;
import com.jichangxiu.system.entity.dto.add.AddSysRole;
import com.jichangxiu.system.entity.dto.edit.EditSysRole;
import com.jichangxiu.system.entity.dto.query.QuerySysRole;
import com.jichangxiu.system.entity.po.SysRole;
import com.jichangxiu.system.entity.po.SysRoleMenu;
import com.jichangxiu.system.entity.po.SysUserRole;
import com.jichangxiu.system.entity.vo.SysRoleVo;
import com.jichangxiu.system.mapper.SysRoleMapper;
import com.jichangxiu.system.service.SysRoleMenuService;
import com.jichangxiu.system.service.SysRoleService;
import com.jichangxiu.system.service.SysUserRoleService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * SysRoleServiceImpl:
 *
 * @author JiChangXiu
 * @create 2024-06-28 13:44:39
 */
@Service
public class SysRoleServiceImpl extends BaseServiceImpl<SysRoleMapper, SysRole, SysRoleVo> implements SysRoleService {

    @Resource
    private SysRoleMenuService sysRoleMenuService;

    @Resource
    private SysUserRoleService sysUserRoleService;

    /**
     * 新增系统角色
     *
     * @param addSysRole 新增系统角色视图对象
     * @return 是否成功
     */
    @Dynamic
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean add(AddSysRole addSysRole) {
        SysRole sysRole = addAndEditValidBo(ConvertUtils.convert(addSysRole, SysRoleBo.class));
        sysRole.setId(IdWorker.getIdStr());
        List<SysRoleMenu> addSysRoleMenuList = new ArrayList<>();
        for (String menuId : addSysRole.getMenuIdList())
            addSysRoleMenuList.add(SysRoleMenu.builder().menuId(menuId).roleId(sysRole.getId()).tenantId(SecurityUtils.getTenantId()).build());
        boolean b1 = getBaseMapper().insert(sysRole) > 0;
        boolean b2 = true;
        if (ObjectUtil.isNotEmpty(addSysRoleMenuList))
            b2 = sysRoleMenuService.saveBatch(addSysRoleMenuList);
        return b1 && b2;
    }

    /**
     * 单（多）选删除系统角色
     *
     * @param idList 系统角色主键列表
     * @return 是否成功
     */
    @Dynamic
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean del(List<String> idList) {
        boolean b1 = getBaseMapper().deleteBatchIds(delValid(idList)) > 0;
        // 跟角色有关的菜单关联关系一起删除
        boolean b2 = sysRoleMenuService.remove(new LambdaQueryWrapper<SysRoleMenu>().in(SysRoleMenu::getRoleId, idList));
        return b1 && b2;
    }

    /**
     * 修改系统角色
     *
     * @param editSysRole 修改系统角色视图对象
     * @return 是否成功
     */
    @Dynamic
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean edit(EditSysRole editSysRole) {
        boolean b1 = getBaseMapper().updateById(addAndEditValidBo(ConvertUtils.convert(editSysRole, SysRoleBo.class))) > 0;
        boolean b2 = true;
        // 数据库中保存的角色菜单关联关系
        List<SysRoleMenu> sysRoleMenuList = sysRoleMenuService.list(new LambdaQueryWrapper<SysRoleMenu>().eq(SysRoleMenu::getRoleId, editSysRole.getId()));
        if (!sysRoleMenuList.stream().map(SysRoleMenu::getMenuId).collect(Collectors.toList()).equals(editSysRole.getMenuIdList()))
            b2 = sysRoleMenuService.addOrDelOrEdit(editSysRole, ConvertUtils.convert(sysRoleMenuList, SysRoleMenuDto.class));
        return b1 && b2;
    }

    /**
     * 根据主键查询系统角色
     *
     * @param id       系统角色主键
     * @param isExtend 是否扩展
     * @return 系统角色详情
     */
    @Dynamic
    @Override
    public SysRoleVo info(@NotBlank(message = "主键不能为空") String id, Boolean isExtend) {
        SysRoleVo sysRoleVo = getBaseMapper().selectJoinOne(SysRoleVo.class, buildQueryWrapper(QuerySysRole.builder().id(id).build()));
        if (Boolean.TRUE.equals(isExtend) && ObjectUtil.isNotEmpty(sysRoleVo))
            sysRoleVo = renderSysRoleVoList(ListUtil.toList(sysRoleVo)).get(0);
        return sysRoleVo;
    }

    /**
     * （可分页）查询系统角色列表
     *
     * @param querySysRole 查询视图对象
     * @return （分页）系统角色列表
     */
    @Dynamic
    @Override
    public Page<SysRoleVo> lists(QuerySysRole querySysRole) {
        Page<SysRoleVo> sysRoleVoPage = getBaseMapper().selectJoinPage(startPage(), SysRoleVo.class, buildQueryWrapper(querySysRole));
        if (Boolean.TRUE.equals(querySysRole.getIsExtend()))
            sysRoleVoPage.setRecords(renderSysRoleVoList(sysRoleVoPage.getRecords()));
        return sysRoleVoPage;
    }

    /**
     * 新增 / 修改 前校验
     *
     * @param sysRoleBo 校验用业务对象
     * @return 数据库操作对象
     */
    private SysRole addAndEditValidBo(SysRoleBo sysRoleBo) {
        SysRole sysRole = ConvertUtils.convert(sysRoleBo, SysRole.class);
        if (ObjectUtil.isNull(sysRole))
            throw new ServiceException("待校验对象为空");
        List<ValidateBos> validateBosList = Stream.of(
                ValidateUtils.createNeValidateBos(ValidateUtils.createValidateBo(SysRole::getId, sysRole.getId(), "主键")),
                ValidateUtils.createOrEqValidateBos(
                        ValidateUtils.createValidateBo(SysRole::getNo, sysRole.getNo(), "编码"),
                        ValidateUtils.createValidateBo(SysRole::getCode, sysRole.getCode(), "标识"),
                        ValidateUtils.createValidateBo(SysRole::getName, sysRole.getName(), "名称"),
                        ValidateUtils.createValidateBo(SysRole::getRemark, sysRole.getRemark(), "备注"))
        ).collect(Collectors.toList());
        // 唯一校验
        ValidateUtils.uniqueValidate(this::countWrapper, validateBosList);
        if (StrUtil.isEmpty(sysRole.getId())) {
            // 新增
            if (Constants.TEN_SUPER_ROLE.equals(sysRole.getCode()))
                throw new ServiceException("标识不能为 " + Constants.TEN_SUPER_ROLE);
            if (Constants.SYS_SUPER_ROLE.equals(sysRole.getCode()))
                throw new ServiceException("标识不能为 " + Constants.SYS_SUPER_ROLE);
            sysRole.setNo(NoUtils.createNo(SecurityUtils.getTenantId(), "role"));
            sysRole.setTenantId(SecurityUtils.getUserInfo().getTenantId());
            return sysRole;
        } else {
            // 修改
            SysRole dbSysRole = getById(sysRole.getId());
            if (Constants.TEN_SUPER_ROLE.equals(sysRole.getCode()) && !sysRole.getCode().equals(dbSysRole.getCode()))
                throw new ServiceException("标识不能为 " + Constants.TEN_SUPER_ROLE);
            if (!dbSysRole.getEnabled().equals(sysRole.getEnabled()) && Constants.TEN_SUPER_ROLE.equals(sysRole.getCode()))
                throw new ServiceException("标识为 " + Constants.TEN_SUPER_ROLE + " 的角色状态不能修改");
            if (Constants.SYS_SUPER_ROLE.equals(sysRole.getCode()) && !sysRole.getCode().equals(dbSysRole.getCode()))
                throw new ServiceException("标识不能为 " + Constants.SYS_SUPER_ROLE);
            if (!dbSysRole.getEnabled().equals(sysRole.getEnabled()) && Constants.SYS_SUPER_ROLE.equals(sysRole.getCode()))
                throw new ServiceException("标识为 " + Constants.SYS_SUPER_ROLE + " 的角色状态不能修改");
            // 当修改了启用状态，并且是不启用得情况下
            if (!sysRole.getEnabled().equals(dbSysRole.getEnabled()) && !sysRole.getEnabled())
                // 如果被用户关联不能修改状态
                if (sysUserRoleService.count(new LambdaQueryWrapper<SysUserRole>().in(SysUserRole::getRoleId, sysRole.getId())) > 0)
                    throw new ServiceException(sysRole.getName() + "该角色已被用户关联，不能修改状态为禁用");
            BeanUtil.copyProperties(sysRole, dbSysRole, true);
            return dbSysRole;
        }
    }

    /**
     * 删除前校验
     *
     * @param idList 主键列表
     * @return 主键列表
     */
    private List<String> delValid(List<String> idList) {
        if (ObjectUtil.isEmpty(idList))
            throw new ServiceException("待校验主键列表为空");
        List<SysRoleBo> sysRoleBoList = selectJoinList(SysRoleBo.class, buildQueryWrapper(QuerySysRole.builder().idList(idList).build()));
        sysRoleBoList.forEach(sysRoleBo -> {
            if (Constants.TEN_SUPER_ROLE.equals(sysRoleBo.getCode()))
                throw new ServiceException("标识为 " + Constants.TEN_SUPER_ROLE + " 的角色不能删除");
            if (Constants.SYS_SUPER_ROLE.equals(sysRoleBo.getCode()))
                throw new ServiceException("标识为 " + Constants.SYS_SUPER_ROLE + " 的角色不能删除");
        });
        // 填充与用户关联的信息
        List<SysUserRole> sysUserRoleList = sysUserRoleService.list(new LambdaQueryWrapper<SysUserRole>().in(SysUserRole::getRoleId, idList));
        sysRoleBoList.forEach(sysRoleBo -> sysRoleBo.setSysUserRoleList(sysUserRoleList.stream().filter(sysUserRole -> sysUserRole.getRoleId().equals(sysRoleBo.getId())).collect(Collectors.toList())));
        sysRoleBoList.forEach(sysRoleBo -> {
            if (ObjectUtil.isNotEmpty(sysRoleBo.getSysUserRoleList()))
                throw new ServiceException(sysRoleBo.getName() + "该角色还被用户绑定，不能删除");
        });
        return idList;
    }

    /**
     * 渲染视图
     *
     * @param sysRoleVoList 视图列表
     * @return 渲染后的视图列表
     */
    private List<SysRoleVo> renderSysRoleVoList(List<SysRoleVo> sysRoleVoList) {
        if (ObjectUtil.isNotEmpty(sysRoleVoList)) {
            // todo 扩展信息填入
            List<SysRoleMenu> sysRoleMenuList = sysRoleMenuService.list(new LambdaQueryWrapper<SysRoleMenu>().in(SysRoleMenu::getRoleId, sysRoleVoList.stream().map(SysRoleVo::getId).collect(Collectors.toList())));
            sysRoleVoList.forEach(sysRoleVo -> sysRoleVo.setMenuIdList(sysRoleMenuList.stream().filter(sysRoleMenu -> sysRoleVo.getId().equals(sysRoleMenu.getRoleId())).map(SysRoleMenu::getMenuId).collect(Collectors.toList())));
        }
        return sysRoleVoList;
    }

    /**
     * 构建查询条件
     *
     * @param querySysRole 查询用业务对象
     * @return 查询条件
     */
    private MPJLambdaWrapper<SysRole> buildQueryWrapper(QuerySysRole querySysRole) {
        MPJLambdaWrapper<SysRole> lambdaQueryWrapper = new MPJLambdaWrapper<SysRole>().selectAll(SysRole.class);
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysRole.getId()), SysRole::getId, querySysRole.getId());
        lambdaQueryWrapper.in(ObjectUtil.isNotEmpty(querySysRole.getIdList()), SysRole::getId, querySysRole.getIdList());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysRole.getNo()), SysRole::getNo, querySysRole.getNo());
        lambdaQueryWrapper.like(StrUtil.isNotEmpty(querySysRole.getCode()), SysRole::getCode, querySysRole.getCode());
        lambdaQueryWrapper.like(StrUtil.isNotEmpty(querySysRole.getName()), SysRole::getName, querySysRole.getName());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysRole.getSort()), SysRole::getSort, querySysRole.getSort());
        lambdaQueryWrapper.like(StrUtil.isNotEmpty(querySysRole.getRemark()), SysRole::getRemark, querySysRole.getRemark());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysRole.getEnabled()), SysRole::getEnabled, querySysRole.getEnabled());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysRole.getEnabledBy()), SysRole::getEnabledBy, querySysRole.getEnabledBy());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysRole.getEnabledTime()), SysRole::getEnabledTime, querySysRole.getEnabledTime());
        lambdaQueryWrapper.apply(StrUtil.isNotEmpty(querySysRole.getScope()), querySysRole.getScope());
        lambdaQueryWrapper.orderByAsc(SysRole::getSort);
        return lambdaQueryWrapper;
    }

}